package com.cyh.testsurfaceview.glsurface1;

import android.graphics.Bitmap;
import android.graphics.SurfaceTexture;
import android.opengl.GLES11Ext;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;
import android.os.Environment;
import android.util.Log;

import com.cyh.testsurfaceview.R;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import static android.opengl.GLES20.glReadPixels;
import static android.opengl.GLES30.*;

public class CustomRender implements GLSurfaceView.Renderer {

    boolean useFront = false;
    float[] matrix = new float[16];
    float[] oes_matrix = new float[16];

    boolean takingPhoto = false;
    boolean recordingVideo = false;

    SurfaceTexture surfaceTexture;
    int[] cameraTexture = new int[1];

    int width, height;

    int[] exportFrame = new int[1];
    int[] exportTexture = new int[1];

    public static final String VERTEX_ATTRIB_POSITION = "a_Position";
    public static final int VERTEX_ATTRIB_POSITION_SIZE = 3;
    public static final String VERTEX_ATTRIB_TEXTURE_POSITION = "a_texCoord";
    public static final int VERTEX_ATTRIB_TEXTURE_POSITION_SIZE = 2;
    public static final String UNIFORM_TEXTURE = "s_texture";
    public static final String UNIFORM_MATRIX = "u_matrix";
//    gl_Position = u_matrix * a_Position;
//    v_texCoord = a_texCoord;

//    outColor = texture(s_texture, v_texCoord);

    public static final float[] vertex ={
            -1f,1f,0.0f,//左上
            -1f,-1f,0.0f,//左下
            1f,-1f,0.0f,//右下
            1f,1f,0.0f//右上
    };

    public static final float[] textureCoord = {
            0.0f,1.0f,
            0.0f,0.0f,
            1.0f,0.0f,
            1.0f,1.0f
    };

    //后置相机，顺时针旋转90度
    public static final float[] textureCoordCameraBack = {
            1.0f, 1.0f,
            0.0f, 1.0f,
            0.0f, 0.0f,
            1.0f, 0.0f
    };

    //前置相机，逆时针旋转90度
    public static final float[] textureCoordCameraFront = {
            0.0f, 0.0f,
            1.0f, 0.0f,
            1.0f, 1.0f,
            0.0f, 1.0f
    };
    public int[] frameBuffer = new int[1];
    public int[] frameTexture = new int[1];

    public int[] beautyBuffer = new int[1];
    public int[] beautyTexture = new int[1];

    //ColorFilter变量
    public static final String UNIFORM_COLOR_FLAG = "colorFlag";
    public static final String UNIFORM_TEXTURE_LUT = "textureLUT";

    public static int COLOR_FLAG = 0;
    public static int COLOR_FLAG_USE_LUT = 6;

    public int hColorFlag;
    public int hTextureLUT;
    private int LUTTextureId;
    //
    private Camera2BasicFragment.CameraHandler mCameraHandler;
    public static int type = 0;

    public FloatBuffer oes_vertexBuffer;
    public FloatBuffer oes_textureCoordBuffer;

    public FloatBuffer color_vertexBuffer;
    public FloatBuffer color_textureCoordBuffer;

    public FloatBuffer beauty_vertexBuffer;
    public FloatBuffer beauty_textureCoordBuffer;

    public FloatBuffer watermark_vertexBuffer;
    public FloatBuffer watermark_textureCoordBuffer;

    public CustomRender(Camera2BasicFragment.CameraHandler cameraHandler) {
        mCameraHandler = cameraHandler;

        oes_vertexBuffer = OpenGLUtil.getFloatBuffer(vertex);
        oes_textureCoordBuffer = useFront ? OpenGLUtil.getFloatBuffer(textureCoordCameraFront)
                : OpenGLUtil.getFloatBuffer(textureCoordCameraBack);

        color_vertexBuffer = OpenGLUtil.getFloatBuffer(vertex);
        color_textureCoordBuffer = OpenGLUtil.getFloatBuffer(textureCoord);

        beauty_vertexBuffer = OpenGLUtil.getFloatBuffer(vertex);
        beauty_textureCoordBuffer = OpenGLUtil.getFloatBuffer(textureCoord);

        watermark_vertexBuffer = OpenGLUtil.getFloatBuffer(vertex);
        watermark_textureCoordBuffer = OpenGLUtil.getFloatBuffer(textureCoord);
    }

    public void setUseFront(boolean useFront) {
        if (this.useFront != useFront) {
            this.useFront = useFront;
            oes_textureCoordBuffer = useFront ? OpenGLUtil.getFloatBuffer(textureCoordCameraFront)
                    : OpenGLUtil.getFloatBuffer(textureCoordCameraBack);
            //前置相机的镜像问题，通过矩阵进行设置
            Matrix.scaleM(matrix, 0, -1, 1, 1);
        }
    }

    public SurfaceTexture getSurfaceTexture() {
        return surfaceTexture;
    }

    public boolean isTakingPhoto() {
        return takingPhoto;
    }

    public void setTakingPhoto(boolean takingPhoto) {
        this.takingPhoto = takingPhoto;
    }

    public boolean isRecordingVideo() {
        return recordingVideo;
    }

    public void setRecordingVideo(boolean recordingVideo) {
        this.recordingVideo = recordingVideo;
    }

    private int oes_program;
    public int oes_hVertex, oes_hMatrix, oes_hTextureCoord, oes_hTexture;
    //a_Position, u_matrix, a_texCoord, s_texture的句柄

    private int color_program;
    public int color_hVertex, color_hMatrix, color_hTextureCoord, color_hTexture;

    //beauty
    private int beauty_program;
    public int beauty_hVertex, beauty_hMatrix, beauty_hTextureCoord, beauty_hTexture;

    private int gHaaCoef;
    private int gHmixCoef;
    private int gHiternum;
    private int gHWidth;
    private int gHHeight;

    private float aaCoef;
    private float mixCoef;
    private int iternum;

    private int mWidth=720;
    private int mHeight=1280;

    //watermark
    private int watermark_program;
    public int watermark_hVertex, watermark_hMatrix, watermark_hTextureCoord, watermark_hTexture;
    private int textureId;

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        glGenTextures(cameraTexture.length, cameraTexture, 0);
        surfaceTexture = new SurfaceTexture(cameraTexture[0]);

        oes_program = OpenGLUtil.createAndLinkProgram(R.raw.texture_vertex_shader, R.raw.texture_oes_fragtment_shader);
        oes_hVertex = glGetAttribLocation(oes_program, VERTEX_ATTRIB_POSITION);
        oes_hMatrix = glGetUniformLocation(oes_program, UNIFORM_MATRIX);
        oes_hTextureCoord = glGetAttribLocation(oes_program, VERTEX_ATTRIB_TEXTURE_POSITION);
        oes_hTexture = glGetUniformLocation(oes_program, UNIFORM_TEXTURE);

        color_program = OpenGLUtil.createAndLinkProgram(R.raw.texture_vertex_shader, R.raw.texture_color_fragtment_shader);
        color_hVertex = glGetAttribLocation(color_program, VERTEX_ATTRIB_POSITION);
        color_hMatrix = glGetUniformLocation(color_program, UNIFORM_MATRIX);
        color_hTextureCoord = glGetAttribLocation(color_program, VERTEX_ATTRIB_TEXTURE_POSITION);
        color_hTexture = glGetUniformLocation(color_program, UNIFORM_TEXTURE);
        LUTTextureId = OpenGLUtil.loadTextureFromRes(R.drawable.amatorka);

        hColorFlag = glGetUniformLocation(color_program, UNIFORM_COLOR_FLAG);
        hTextureLUT = glGetUniformLocation(color_program, UNIFORM_TEXTURE_LUT);

        beauty_program = OpenGLUtil.createAndLinkProgram(R.raw.beauty_vertex_shader, R.raw.beauty_fragment_shader);
        beauty_hVertex = glGetAttribLocation(beauty_program, VERTEX_ATTRIB_POSITION);
        beauty_hMatrix = glGetUniformLocation(beauty_program, UNIFORM_MATRIX);
        beauty_hTextureCoord = glGetAttribLocation(beauty_program, VERTEX_ATTRIB_TEXTURE_POSITION);
        beauty_hTexture = glGetUniformLocation(beauty_program, UNIFORM_TEXTURE);

        gHaaCoef=GLES20.glGetUniformLocation(beauty_program,"aaCoef");
        gHmixCoef=GLES20.glGetUniformLocation(beauty_program,"mixCoef");
        gHiternum=GLES20.glGetUniformLocation(beauty_program,"iternum");
        gHWidth=GLES20.glGetUniformLocation(beauty_program,"mWidth");
        gHHeight=GLES20.glGetUniformLocation(beauty_program,"mHeight");

        watermark_program = OpenGLUtil.createAndLinkProgram(R.raw.texture_vertex_shader, R.raw.texture_fragtment_shader);
        watermark_hVertex = glGetAttribLocation(beauty_program, VERTEX_ATTRIB_POSITION);
        watermark_hMatrix = glGetUniformLocation(beauty_program, UNIFORM_MATRIX);
        watermark_hTextureCoord = glGetAttribLocation(beauty_program, VERTEX_ATTRIB_TEXTURE_POSITION);
        watermark_hTexture = glGetUniformLocation(beauty_program, UNIFORM_TEXTURE);
        textureId = OpenGLUtil.loadTextureFromRes(R.mipmap.logo);

        Matrix.setIdentityM(matrix, 0);
        Matrix.scaleM(matrix, 0,1, -1, 1);//-1表示要翻转

        Matrix.setIdentityM(oes_matrix, 0);

        mCameraHandler.sendMessage(mCameraHandler.obtainMessage(
                Camera2BasicFragment.CameraHandler.MSG_SET_SURFACE_TEXTURE, 0));
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        glViewport(0, 0, width, height);
        if (this.width != width || this.height != height) {
            this.width = width;
            this.height = height;

            //Matrix.orthoM(oes_matrix, 0, -1f, 1f, -1, 1, -1f, 7f);

            createBindFrameBufferTexture(frameBuffer, frameTexture);
            createBindFrameBufferTexture(beautyBuffer, beautyTexture);
            createBindFrameBufferTexture(exportFrame, exportTexture);

        }
    }

    public void createBindFrameBufferTexture(int[] frameBuffer, int [] frameTexture) {
        //创建并初始化FBO纹理
        // 创建一个 2D 纹理用于连接 FBO 的颜色附着
        glGenTextures(frameTexture.length, frameTexture, 0);
        glBindTexture(GL_TEXTURE_2D, frameTexture[0]);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glBindTexture(GL_TEXTURE_2D, GL_NONE);

        //创建并初始化FBO
        // 创建 FBO
        glGenFramebuffers(frameBuffer.length, frameBuffer, 0);
        // 绑定 FBO
        glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer[0]);
        // 绑定 FBO 纹理
        glBindTexture(GL_TEXTURE_2D, frameTexture[0]);
        // 将纹理连接到 FBO 附着
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, frameTexture[0], 0);
        // 分配内存大小
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, null);
        glBindTexture(GL_TEXTURE_2D, GL_NONE);
        glBindFramebuffer(GL_FRAMEBUFFER, GL_NONE);
    }

    @Override
    public void onDrawFrame(GL10 gl) {
        if (surfaceTexture != null) {
            surfaceTexture.updateTexImage();
        }

        //离屏渲染
        if (type == 0) {
            glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer[0]);
        } else {
            glBindFramebuffer(GL_FRAMEBUFFER, beautyBuffer[0]);
        }
        //glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer[0]);
        //glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, frameTexture[0], 0);

        // 选定离屏渲染的 Program，绑定 VAO 和图像纹理，进行绘制（离屏渲染）
        // m_ImageTextureId 为另外一个用于纹理映射的图片纹理
        glUseProgram(oes_program);

        // Set the texture.
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, cameraTexture[0]);

        // Copy the texture transformation matrix over.
        glUniformMatrix4fv(oes_hMatrix, 1, false, oes_matrix, 0);

        // Enable the "aPosition" vertex attribute. Connect vertexBuffer to "aPosition".
        glEnableVertexAttribArray(oes_hVertex);
        glVertexAttribPointer(oes_hVertex, VERTEX_ATTRIB_POSITION_SIZE,
                GL_FLOAT, false, 0, oes_vertexBuffer);

        glEnableVertexAttribArray(oes_hTextureCoord);
        glVertexAttribPointer(oes_hTextureCoord, VERTEX_ATTRIB_TEXTURE_POSITION_SIZE,
                GL_FLOAT, false, 0, oes_textureCoordBuffer);

        glUniform1i(oes_hTexture, 0);

        glDrawArrays(GL_TRIANGLE_FAN,0,vertex.length / 3);

        glDisableVertexAttribArray(oes_hVertex);
        glDisableVertexAttribArray(oes_hTextureCoord);

        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        //

        if (isTakingPhoto()) {
            ByteBuffer exportBuffer = ByteBuffer.allocate(width * height * 4);

            glBindFramebuffer(GL_FRAMEBUFFER, exportFrame[0]);
            //glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, exportTexture[0], 0);

            //在屏幕预览时y轴正方向是向下的，所以保存到本地的时候需要在y轴上做一次反转。
            Matrix.scaleM(matrix,0,1,-1,1);
            if (type == 0) {
                drawColorFilter();
            } else {
                drawBeautyFilter();
                drawWatermarkFilter();
            }
            glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, exportBuffer);
            savePhoto(exportBuffer);

            glBindFramebuffer(GL_FRAMEBUFFER, 0);

            setTakingPhoto(false);
            Matrix.scaleM(matrix,0,1,-1,1);
        } else {
            //普通渲染
            if (type == 0) {
                drawColorFilter();
            } else {
                drawBeautyFilter();
                drawWatermarkFilter();
            }
        }
    }

    public void drawColorFilter(){
// 完成离屏渲染后，结果图数据便保存在我们之前连接到 FBO 的纹理 m_FboTextureId 。
// 我们再拿 FBO 纹理 m_FboTextureId 做一次普通渲染便可将之前离屏渲染的结果绘制到屏幕上。
// 这里我们编译连接了 2 个 program ,一个用作离屏渲染的 m_FboProgramObj，一个用于普通渲染的 m_ProgramObj

//选定另外一个着色器程序，以 m_FboTextureId 纹理作为输入进行普通渲染
        glUseProgram(color_program);
        glUniformMatrix4fv(color_hMatrix, 1, false, matrix,0);

        glActiveTexture(GL_TEXTURE0);
        //绑定 FBO 纹理
        glBindTexture(GL_TEXTURE_2D, frameTexture[0]);

        glUniform1i(hColorFlag, COLOR_FLAG);
        glUniform1i(color_hTexture, 0);

        if (COLOR_FLAG == COLOR_FLAG_USE_LUT){
            glActiveTexture(GL_TEXTURE0 + 1);
            glBindTexture(GL_TEXTURE_2D, LUTTextureId);
            glUniform1i(hTextureLUT, 1);
        }

        glEnableVertexAttribArray(color_hVertex);
        glVertexAttribPointer(color_hVertex, VERTEX_ATTRIB_POSITION_SIZE,
                GL_FLOAT, false, 0, color_vertexBuffer);

        glEnableVertexAttribArray(color_hTextureCoord);
        glVertexAttribPointer(color_hTextureCoord, VERTEX_ATTRIB_TEXTURE_POSITION_SIZE,
                GL_FLOAT, false, 0, color_textureCoordBuffer);

        glDrawArrays(GL_TRIANGLE_FAN,0,vertex.length / 3);

        glDisableVertexAttribArray(color_hVertex);
        glDisableVertexAttribArray(color_hTextureCoord);
    }

    public void drawBeautyFilter(){
        glUseProgram(beauty_program);
        glUniformMatrix4fv(beauty_hMatrix, 1, false, matrix,0);

        glActiveTexture(GL_TEXTURE0);
        //绑定 FBO 纹理
        glBindTexture(GL_TEXTURE_2D, beautyTexture[0]);

        glUniform1i(beauty_hTexture, 0);

        GLES20.glUniform1i(gHWidth,mWidth);
        GLES20.glUniform1i(gHHeight,mHeight);
        GLES20.glUniform1f(gHaaCoef,aaCoef);
        GLES20.glUniform1f(gHmixCoef,mixCoef);
        GLES20.glUniform1i(gHiternum,iternum);

        glEnableVertexAttribArray(beauty_hVertex);
        glVertexAttribPointer(beauty_hVertex, VERTEX_ATTRIB_POSITION_SIZE,
                GL_FLOAT, false, 0, beauty_vertexBuffer);

        glEnableVertexAttribArray(beauty_hTextureCoord);
        glVertexAttribPointer(beauty_hTextureCoord, VERTEX_ATTRIB_TEXTURE_POSITION_SIZE,
                GL_FLOAT, false, 0, beauty_textureCoordBuffer);

        glDrawArrays(GL_TRIANGLE_FAN,0,vertex.length / 3);

        glDisableVertexAttribArray(beauty_hVertex);
        glDisableVertexAttribArray(beauty_hTextureCoord);
    }

    public void drawWatermarkFilter(){
        GLES20.glDisable(GLES20.GL_DEPTH_TEST);
        GLES20.glEnable(GLES20.GL_BLEND);
        GLES20.glBlendFunc(GLES20.GL_SRC_COLOR,GLES20.GL_DST_ALPHA);

        float[] watermark_matrix = new float[16];
        Matrix.setIdentityM(watermark_matrix, 0);
        Matrix.scaleM(watermark_matrix, 0,1, -1, 1);
        Matrix.scaleM(watermark_matrix,0, 0.2f, 0.2f, 1);
        Matrix.translateM(watermark_matrix, 0,2f, 2f, 1);

        glUseProgram(watermark_program);
        glUniformMatrix4fv(watermark_hMatrix, 1, false, watermark_matrix,0);

        glActiveTexture(GL_TEXTURE0);
        //绑定 FBO 纹理
        glBindTexture(GL_TEXTURE_2D, textureId);

        glUniform1i(watermark_hTexture, 0);

        glEnableVertexAttribArray(watermark_hVertex);
        glVertexAttribPointer(watermark_hVertex, VERTEX_ATTRIB_POSITION_SIZE,
                GL_FLOAT, false, 0, watermark_vertexBuffer);

        glEnableVertexAttribArray(watermark_hTextureCoord);
        glVertexAttribPointer(watermark_hTextureCoord, VERTEX_ATTRIB_TEXTURE_POSITION_SIZE,
                GL_FLOAT, false, 0, watermark_textureCoordBuffer);

        glDrawArrays(GL_TRIANGLE_FAN,0,vertex.length / 3);

        glDisableVertexAttribArray(watermark_hVertex);
        glDisableVertexAttribArray(watermark_hTextureCoord);

        GLES20.glDisable(GLES20.GL_BLEND);
    }

    public void setFlag(int flag) {
        //super.setFlag(flag);
        switch (flag){
            case 1:
                a(1,0.19f,0.54f);
                break;
            case 2:
                a(2,0.29f,0.54f);
                break;
            case 3:
                a(3,0.17f,0.39f);
                break;
            case 4:
                a(3,0.25f,0.54f);
                break;
            case 5:
                a(4,0.13f,0.54f);
                break;
            case 6:
                a(4,0.19f,0.69f);
                break;
            default:
                a(0,0f,0f);
                break;
        }
    }

    private void a(int a,float b,float c){
        this.iternum=a;
        this.aaCoef=b;
        this.mixCoef=c;
    }

    public void savePhoto(final ByteBuffer buffer) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
                bitmap.copyPixelsFromBuffer(buffer);
                String folderPath = Environment.getExternalStorageDirectory() + "/DCIM/Camera/";
                File folder = new File(folderPath);
                if (!folder.exists() && !folder.mkdirs()) {
                    Log.e("demos", "图片目录异常");
                    return;
                }
                String filePath = folderPath + System.currentTimeMillis() + ".jpg";
                BufferedOutputStream bos = null;
                try {
                    FileOutputStream fos = new FileOutputStream(filePath);
                    bos = new BufferedOutputStream(fos);
                    bitmap.compress(Bitmap.CompressFormat.JPEG, 100, bos);
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                } finally {
                    if (bos != null) {
                        try {
                            bos.flush();
                            bos.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    if (bitmap != null) {
                        bitmap.recycle();
                    }
                }
            }
        }).start();
    }
}
